package de.tu_darmstadt.gdi1.battleship.controller;

import java.io.BufferedReader;

import java.util.Random;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.MessageFormat;

import acm.io.IODialog;
import de.tu_darmstadt.gdi1.battleship.exceptions.BoatFailException;
import de.tu_darmstadt.gdi1.battleship.exceptions.FieldAlreadyShotException;
import de.tu_darmstadt.gdi1.battleship.exceptions.InternalFailureException;
import de.tu_darmstadt.gdi1.battleship.exceptions.InvalidCoordinatesException;
import de.tu_darmstadt.gdi1.battleship.exceptions.InvalidLevelFileException;
import de.tu_darmstadt.gdi1.battleship.exceptions.NoAmmoException;
import de.tu_darmstadt.gdi1.battleship.exceptions.NotUsersTurnException;
import de.tu_darmstadt.gdi1.battleship.exceptions.NumberFailException;
import de.tu_darmstadt.gdi1.battleship.model.BattleshipGame;
import de.tu_darmstadt.gdi1.battleship.model.Coordinates;
import de.tu_darmstadt.gdi1.battleship.view.HighscoreFrame;
import de.tu_darmstadt.gdi1.battleship.view.Messages;
import de.tu_darmstadt.gdi1.battleship.view.Skin;
import de.tu_darmstadt.gdi1.battleship.view.StudentWindow;

public class UserInteraction {

	protected BattleshipGame bsg;
	protected StudentWindow mainWnd;
	private HighscoreFrame highscoreFrame;

	// zum manuellen Platzieren
	private boolean placeShips = false;
	private int actLength;
	private int actCount;
	private boolean v;

	public UserInteraction(BattleshipGame battleshipGame, StudentWindow wnd) {
		this.bsg = battleshipGame;
		this.mainWnd = wnd;
	}

	/**
	 * detects when a player clicked on the own field or the enemy's field. the
	 * available clicks depend on the current mode
	 * 
	 * @param posX
	 *            x coordinate of own or enemy's field
	 * @param posY
	 *            y coordinate of own or enemy's field
	 */
	public void userClicked(int posX, int posY) {
		
		// place mode
		if (placeShips) {
			placeModeClick(posX, posY);
		}
		// normal game
		else {
			//VaseCheat
			if (posX==10&&posY==2)
				//aber nich immer
				{Random r=new Random();
				int z=r.nextInt(10);
				if(z==0){
				mainWnd.getGamePanel()
				.loadSkin(
						new Skin(
								new File(
										"src/de/tu_darmstadt/gdi1/resources/images/vase")));

		try {
			mainWnd.getGamePanel().redraw();
		} catch (InternalFailureException e) {
			return;
		}
		;}}
			if (bsg.isFinished() && 
					(bsg.getHumanMap().countLeftParts()==0 || bsg.getCpuMap().countLeftParts()==0)){
				mainWnd.showMessage(Messages.getString("Info.end"));
				return;
			}
			// ammunition mode
			if (bsg.isAmmunitionMode()) {
				if (bsg.getTochoose() == true) {
					choosePartClick(posX, posY);
				} else {
					playerShotClick(posX, posY);
				}
			}
			// non ammunition mode
			else {
				playerShotClick(posX, posY);
			}

			// If game has ended notify Player and stop.
			if (bsg.isFinished() && bsg.getWinner() == 1) {
				// message
				Object[] args = { bsg.getPlayerName(),
						new Integer(bsg.getShotsUsed()) };
				MessageFormat formatter = new MessageFormat("");
				formatter.applyPattern(Messages.getString("Info.won"));
				mainWnd.showMessage(formatter.format(args));

				getHighscoreFrame().update();
				getHighscoreFrame().setVisible(true);

				return;
			}

			// Start Thread that can be suspended for 1 Second.
			new Thread() {

				public void run() {
					// 1 Sekunde warten
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}

					// CPU shoots ,if it fails repeat!
					boolean success = false;
					while (!success) {
						try {
							bsg.cpuShoot();
							success = true;
							mainWnd.getGamePanel().redraw();
						} catch (InvalidCoordinatesException e) {
							System.err.println(e.getMessage());
						} catch (FieldAlreadyShotException e) {
							System.err.println(e.getMessage());
						} catch (InternalFailureException e) {
							System.err.println(Messages
									.getString("Info.redrawErr"));
						}
					}

					// If game has ended notify Player and stop.
					if (bsg.isFinished() && bsg.getWinner() == 2) {
						Object[] args = { bsg.getPlayerName(),
								new Integer(bsg.getShotsUsed()) };
						MessageFormat formatter = new MessageFormat("");
						formatter.applyPattern(Messages.getString("Info.lost"));
						mainWnd.showMessage(formatter.format(args));

						return;
					}
				}
			}.start();

		}
	}

	private HighscoreFrame getHighscoreFrame() {
		if (highscoreFrame == null) {
			highscoreFrame = new HighscoreFrame(bsg);
		}
		return highscoreFrame;
	}

	/**
	 * clicks are used to place ships on the own field
	 * 
	 * @param x
	 *            x coordinate of the field
	 * @param y
	 *            y coordinate of the field
	 */
	private void placeModeClick(int x, int y) {
		if (x > 9) {
			mainWnd.showMessage(Messages.getString("Info.placeWrongMap"));
		} else {
			bsg.getHumanMap().tLockFields(actLength, v);
			if (bsg.getHumanMap().getEntry(x, y).isPermLock()
					|| bsg.getHumanMap().getEntry(x, y).isTempLock()) {
				// alternativ passiert gar nichts
				mainWnd.showMessage(Messages.getString("Info.placeFail"));
				bsg.getHumanMap().unlockTemp();
			} else {

				bsg.getHumanMap().placeShip(x, y, actLength, v);
				actCount++;
				try {
					mainWnd.getGamePanel().redraw();
				} catch (InternalFailureException e1) {
					System.err.println(Messages.getString("Info.redrawErr"));
				}
				if (actLength + actCount > 5) {
					actLength--;
					actCount = 0;
				}
				bsg.getHumanMap().unlockTemp();
				if (actLength == 1) {
					mainWnd.showMessage(Messages.getString("Info.fin"));
					bsg.getHumanMap().unlockPerm();
					try {
						bsg.getHumanMap().localizeShips();
					} catch (BoatFailException e) {
						System.err.println(Messages
								.getString("Info.shipProbFin"));
					} catch (NumberFailException e) {
						System.err.println(Messages
								.getString("Info.shipProbNum"));
					}
					placeShips = false;
					mainWnd.showMessage(Messages.getString("Info.shipSelect"));
				}
			}
		}
	}

	/**
	 * ammunition mode method where you choose your ship to shoot
	 * 
	 * @param x
	 *            x coordinate own field
	 * @param y
	 *            y coordinate own field
	 */
	public void choosePartClick(int x, int y) {
		try {
			bsg.chooseShipToShoot(new Coordinates(x, y));
			if (bsg.getTochoose()) {
				mainWnd.showMessage(Messages.getString("Info.noAmmo"));
			} else {
				Object[] args = { bsg.getHumanMap().getMap()[x][y]
						.getAmmunition() };
				MessageFormat formatter = new MessageFormat(
						Messages.getString("Info.ammoLeft"));
				mainWnd.showMessage(formatter.format(args));

			}

		} catch (InvalidCoordinatesException e) {
			mainWnd.showMessage(Messages.getString("Info.noShipChosen"));
		} catch (NoAmmoException e) {
			mainWnd.showMessage(Messages.getString("Info.noAmmo"));
		}

	}

	/**
	 * method to choose where your next hit is directed to
	 * 
	 * @param x
	 *            x coordinate enemy's field
	 * @param y
	 *            y coordinate enemy's field
	 */
	public void playerShotClick(int x, int y) {
		// to work only in enemy Map, shift X to the right.
		x -= 11;

		try {// player shoots
			bsg.playerShoot(new Coordinates(x, y));
		} catch (NotUsersTurnException e) {
			mainWnd.showMessage(Messages.getString("Info.notUsersTurn"));
		} catch (InvalidCoordinatesException e) {
			mainWnd.showMessage(Messages.getString("Info.invCoords"));
		} catch (FieldAlreadyShotException e) {
			mainWnd.showMessage(Messages.getString("Info.isShot"));
		}

		// renew output
		try {
			mainWnd.getGamePanel().redraw();
		} catch (InternalFailureException e) {
			System.err.println(Messages.getString("Info.redrawErr"));
		}

	}

	/**
	 * Method that starts a new premade leve
	 */
	public void userPressedN() {
		IODialog dial = new IODialog();
		Boolean choice = dial.readBoolean(Messages.getString("Info.startNew"),
				Messages.getString("Info.startOK"),
				Messages.getString("Info.startLeave"));
		if (choice) {
			loadRandomStandardLevel();
		}
	}

	/**
	 * loads an already generated level
	 */
	public void loadRandomStandardLevel() {
		try {
			placeShips = false;
			bsg.loadRandomLvl();
			mainWnd.getGamePanel().redraw();
		} catch (IOException e) {
			System.err.println(Messages.getString("Info.loadFail"));
		} catch (InvalidLevelFileException e) {
			System.err.println(Messages.getString("Info.lvlInval"));
		} catch (BoatFailException e) {
			System.err.println(Messages.getString("Info.shipProbFin"));
		} catch (NumberFailException e) {
			System.err.println(Messages.getString("Info.shipProbNum"));
		} catch (InternalFailureException e) {
			System.err.println("Internal Failure.");
		}
	}

	/**
	 * creates a random level
	 */
	public void generateRandomLevel() {
		try {
			placeShips = false;
			bsg.createRandomLvl();
			mainWnd.getGamePanel().redraw();
		} catch (BoatFailException e) {
			System.err.println(Messages.getString("Info.shipProbFin"));
		} catch (NumberFailException e) {
			System.err.println(Messages.getString("Info.shipProbNum"));
		} catch (InternalFailureException e) {
			System.err.println("Internal Failure.");
		}
	}

	/**
	 * method which initializes the ship placement
	 */
	public void placeShips() {
		mainWnd.showMessage(Messages.getString("Info.placement"));
		placeShips = true;
		actLength = 5;
		v = true;
		try {
			bsg.reset();
			bsg.getCpuMap().generateRandomMap();
			bsg.getCpuMap().localizeShips();
			bsg.getHumanMap().clear();
			bsg.getHumanMap().unlockPerm();
			bsg.getHumanMap().unlockTemp();
			mainWnd.getGamePanel().redraw();

		} catch (BoatFailException e) {
			System.err.println(Messages.getString("Info.shipProbFin"));
		} catch (NumberFailException e) {
			System.err.println(Messages.getString("Info.shipProbNum"));
		} catch (InternalFailureException e) {
			System.err.println("Internal Failure.");
		}

	}

	/**
	 * loads a saved skin from a folder
	 */
	public void loadSkinFromFolder() {
		File f = mainWnd.getFolder(null);
		if (f != null) {
			mainWnd.getGamePanel().loadSkin(new Skin(f));
			mainWnd.showMessage(Messages.getString("Info.loadSuc"));
		} else {
			mainWnd.showMessage(Messages.getString("Info.noFolder"));
		}
		try {
			mainWnd.getGamePanel().redraw();
		} catch (InternalFailureException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * method for resetting the rotation of a ship after pressed v and ship set
	 */
	public void userPressedV() {
		v = !v;
	}

	/**
	 * loads an already generated level from a folder
	 */
	public void loadLevelFromFile() {

		placeShips = false;

		File f = mainWnd.getFile(new File(
				"src/de/tu_darmstadt/gdi1/resources/levels/defaultlevels"));
		if (f != null) {
			try {
				bsg.readLvlFromFile(f);
			} catch (IOException e) {
				mainWnd.showMessage(Messages.getString("Info.loadFail"));
			} catch (InvalidLevelFileException e) {
				mainWnd.showMessage(Messages.getString("Info.lvlInval"));
			}
			mainWnd.showMessage(Messages.getString("Info.loadSuc"));
		} else {
			mainWnd.showMessage(Messages.getString("Info.noFolder"));
		}
		try {
			mainWnd.getGamePanel().redraw();
		} catch (InternalFailureException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/**
	 * saves the current game
	 * 
	 * @param f
	 *            file where it will be written
	 */
	public void saveCurrentGameToFile(File f) {

		try {
			if (f.exists() && f.canWrite()) {
				f.delete();
			}

			f.createNewFile();
			FileWriter fW = new FileWriter(f);
			BufferedWriter bW = new BufferedWriter(fW, 4);
			// StringReader sR = new StringReader(bsg.generateSaveString())
			// BufferedReader bR = new BufferedReader(sR);
			try {
				bW.write(bsg.generateSaveString());
			} finally {
				bW.close();
			}

		} catch (IOException e) {
			mainWnd.showMessage(Messages.getString("Info.noSave"));
		}
	}

	/**
	 * saves, overwrites a saved game or creates one depends on the user's input
	 */
	public void saveCurrentGameToFile() {
		// TODO: Standardordner fuer Spielstaende?!
		if (!placeShips) {
			File f = mainWnd.getFile(null);
			if (f != null) {
				saveCurrentGameToFile(f);
			}
		} else {
			mainWnd.showMessage(Messages.getString("Info.noSaveNow"));
		}

	}

	/**
	 * loads a game form a file
	 * 
	 * @param f
	 *            file with the save data
	 */
	public void loadSavedGame(File f) {

		try {
			if (f.exists() && f.canRead()) {
				// Place BufferedReader on Levelfile
				FileReader saveFileReader = new FileReader(f);
				BufferedReader br = new BufferedReader(saveFileReader);

				try {
					bsg.loadSaveString(br);
				} finally {
					saveFileReader.close();
				}

				mainWnd.notifyLevelLoaded(21, 10);
			}
		} catch (Exception e) {
			mainWnd.showMessage(Messages.getString("Info.loadFail"));
			e.printStackTrace();
		}
	}

	/**
	 * loads a game from a file which the user choosed
	 */
	public void loadSavedGame() {
		File f = mainWnd.getFile(null);
		if (f != null) {
			loadSavedGame(f);
		}
	}

	/**
	 * exit Battleshipgame
	 */
	public void exitProgramm() {
		System.exit(0);
	}

	
	
	

	/**
	 * returns the HighscoreFrame
	 * 
	 * @return the HighscoreFrame
	 */
	public HighscoreFrame getHighScoreFrame() {
		return highscoreFrame;
	}

	/**
	 * sets the mode for the Mouseover in StudentPanel
	 */
	public void setPPMode(int x, int y) {	
		if (placeShips){
			if(x>9){ mainWnd.getGamePanel().setplaceMode(1);}
			else{
				bsg.getHumanMap().tLockFields(actLength, v);

				if (bsg.getHumanMap().getEntry(x, y).isPermLock()
						|| bsg.getHumanMap().getEntry(x, y).isTempLock()) {
					//can't place here
					mainWnd.getGamePanel().setplaceMode(1);  
				}else{
					if(v){
						mainWnd.getGamePanel().setplaceMode(actLength);
					}else{
						mainWnd.getGamePanel().setplaceMode(actLength+10);
					}
				}}
		}else{
			mainWnd.getGamePanel().setplaceMode(0); 
		}
		bsg.getHumanMap().unlockTemp();
	}

	public void saveLevelToFile() {
		
		if (!placeShips) {
			File f = mainWnd.getFile(null);
			if (f != null) {
				try {
					if (f.exists() && f.canWrite()) {
						f.delete();
					}

					f.createNewFile();
					FileWriter fW = new FileWriter(f);
					BufferedWriter bW = new BufferedWriter(fW, 4);
					try {
						bW.write(bsg.getVirginLevelString());
					} finally {
						bW.close();
					}

				} catch (IOException e) {
					mainWnd.showMessage(Messages.getString("Info.noSave"));
				}
				
			}
		} else {
			mainWnd.showMessage(Messages.getString("Info.noSaveNow"));
		}
		
	}

}
